/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/

// 定义状态
const PENDING   = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED  = 'rejected'

class MyPromise {
    status = PENDING   
    value = null  
    reason = null   

    fulfilledQueue = []  
    rejectQueue  = []  

    constructor (executor) {
        // 捕获执行代码中的报错
        try {
          // 立即执行执行器
          executor(this.resolve, this.reject)
        } catch (e) {
          this.reject(e) // 直接抛出异常
        }
    }

    resolve = value => {
        // 如果状态已经修改，不能继续修改
        if (this.status !== PENDING) return
        this.status = FULFILLED
        this.value = value
		
        // 循环执行缓存回调队列
        while (this.fulfilledQueue.length) {
            this.fulfilledQueue.shift()()
        }
    }
    
    reject = reason => {
        /// 如果状态已经修改，不能继续修改
        if (this.status !== PENDING) return
        this.status = REJECTED
        this.reason = reason
        
        // 循环执行缓存回调队列
        while (this.rejectQueue.length) {
            this.rejectQueue.shift()()
        }
    }
	
    then (onFulfilled, onRejected) {
        // 参数不是函数，就给默认值
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
        onRejected  = typeof onRejected  === 'function' ? onRejected : reason => {throw reason}
        console.log('then init', onFulfilled)
        const promise2 =  new MyPromise((resolve, reject) => {
            let resolvePromise = cb => {
                console.log('创建 Microtask > cb=', cb);
                queueMicrotask(() => {
                    try {
                      if (x === promise2) {
                        // 类型错误，不如 Promise A+ 测试报错
                        return reject(new TypeError('循环调用~'))
                      }
                      let x = cb(this.value)
                      console.log('执行 Microtask > x=',x)
                      // 如果是 null 或者  undefined 直接返回
                      if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
                        if (x instanceof MyPromise) {
                          // x.then(resolve, reject)
                          x.then(val => {
                            console.log('x.then > value=',val)
                            // 这里依然需要思考
                            resolvePromise(()=>val)
                          }, reason => {
                            resolvePromise(()=>reason)
                          })
                        } else {
                          console.log('resolve x='+x)
                          resolve(x)
                        }
                      } else {
                        resolve(x)
                      }
                    } catch (e) {
                      reject(e)
                    }
                })
            }
            console.log(this.status)
            if (this.status === FULFILLED) {
                resolvePromise(onFulfilled)
            } else if (this.status === REJECTED) {
                resolvePromise(onRejected)
            } else if (this.status === PENDING) {
              // pending 状态先缓存事件
              this.fulfilledQueue.push( resolvePromise.bind(this, onFulfilled) )
              this.rejectQueue.push( resolvePromise.bind(this, onRejected) )
            }
        })
        return promise2
    }

    static all (arr) {
        const result = []
        let count = 0
        return new Promise((resolve, reject) => {
            let addData = (i, val) => {
                result[i] = val
                count++
                if (count === arr.length) {
                    resolve(result)
                }
            }
            arr.forEach((item, index) => {
                if (item instanceof MyPromise) {
                    item.then(value=>addData(index, value), reject)
                } else {
                    addData(index, item)
                }
            })
        })
    }

    static race (arr) {
        return new MyPromise((resolve, reject) => {
            arr.forEach(item => {
                if (item instanceof MyPromise) {
                    item.then(resolve, reject)
                } else {
                    queueMicrotask(() => {
                        resolve(item)
                    })
                }
            })
        })
    }

    static resolve (param) {
        if (param instanceof MyPromise) return param
        return new MyPromise(resolve => resolve(param))
    }

    static reject (param) {
        return new MyPromise((resolve, reject) => reject(param))
    }

    finally (callback) {
        let x = typeof callback === 'function' ? callback() : callback
        return MyPromise.resolve(x).then(()=>this, reason=>{throw reason})
    }

    catch (onRejected) {
        return this.then(null, onRejected)
    }
}

MyPromise.deferred = function() {
    var result = {};
    result.promise = new MyPromise(function(resolve, reject){
        result.resolve = resolve;
        result.reject = reject;
    });
    return result;
}

module.exports = MyPromise